#include "filetransfer.h"
#include <QDataStream>
#include <QGridLayout>
#include <QMessageBox>
#include <QFileDialog>
#include <QFileInfo>
#include <QDebug>
#include <QTimer>


//this is the constructor used
//for when they are sending a file

TransferBox::TransferBox(int id, QString filepath, QWidget *parent)
        : QGroupBox(parent)
{
    this->file = new QFile(filepath, this);
    this->file->open(QIODevice::ReadOnly);
    this->fileSize = this->file->size();
    this->construct(id, QFileInfo(filepath).fileName(), TransferBox::SendDirection);
}

TransferBox::TransferBox(int id, QString filename, quint64 filesize, QWidget *parent)
        : QGroupBox(parent)
{
    this->fileSize = filesize;
    this->file = new QFile(filename, this);
    this->file->open(QIODevice::WriteOnly);
    this->construct(id, filename, TransferBox::ReceiveDirection);
}

TransferBox::~TransferBox()
{
    this->file->close();
}


void TransferBox::construct(int id, QString filename, TransferBox::Direction dir)
{

    this->id = id;
    this->direction = dir;
    this->transferred = 0;

    if(dir == TransferBox::SendDirection)
        this->setTitle(tr("Sending: ") +  filename);
    else if (dir == TransferBox::ReceiveDirection)
        this->setTitle(tr("Receiving: ") + filename);
    else
        qFatal("Hmmm, invalid direction?");



    this->leftButton = new QPushButton(tr("Pause"), this);
    this->rightButton = new QPushButton(tr("Cancel"), this);

    this->progressBar = new QProgressBar(this);
        //let's make it like a percentage (100 increments)
        this->progressBar->setMinimum(0);
        this->progressBar->setMaximum(100);
        this->progressBar->setFormat(tr("Initializing"));

    this->statusLabel = new QLabel(tr("starting"), this);

    QGridLayout *lay = new QGridLayout(this);
    lay->addWidget(this->progressBar, 1, 1, 1, 2);
    lay->addWidget(this->statusLabel, 2, 1, 1, 2);
    lay->addWidget(this->leftButton, 3, 1, 1, 1);
    lay->addWidget(this->rightButton, 3, 2, 1, 1);


    if(this->fileSize < Q_UINT64_C(10000)) //9,999 bytes or less
    {
       this->sizeDivisor = Q_UINT64_C(1);
       this->postfix = "B"; //Bytes
    }
    else if(this->fileSize < Q_UINT64_C(10240000)) //9,999 KiB or less
    {
       this->sizeDivisor = Q_UINT64_C(1024);
       this->postfix = "KiB";
    }
    else if(this->fileSize < Q_UINT64_C(10485760000)) //9,999 MiB or less
    {
       this->sizeDivisor = Q_UINT64_C(1048576);
       this->postfix = "MiB";
    }
    else if(this->fileSize < Q_UINT64_C(1073741824000)) //9,999 GiB or less
    {
       this->sizeDivisor = Q_UINT64_C(1073741824);
       this->postfix = "GiB";
    }
    else //9,999 TiB or less (and lets not go further, lol)
    {
       this->sizeDivisor = Q_UINT64_C(10995116277760000);
       this->postfix = "TiB";
    }

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(updateProgress()));
    timer->start(500);

    //and call it right away..
    this->updateProgress();

}

void TransferBox::updateProgress()
{

    //filesize in units is
    int totalSize = this->fileSize / this->sizeDivisor;
    int doneSize = this->transferred / this->sizeDivisor;

    QString format("%1 / %2 %3");
    format = format.arg(doneSize).arg(totalSize).arg(this->postfix);
    this->progressBar->setValue((this->transferred * 100) / this->fileSize);
    this->progressBar->setFormat(format);
    this->statusLabel->setText(format);
}

void TransferBox::receive(QByteArray bytes)
{
    Q_ASSERT(this->direction == TransferBox::ReceiveDirection);

    this->file->write(bytes);
    this->transferred += bytes.size();
}
QByteArray TransferBox::send()
{
    Q_ASSERT(this->direction == TransferBox::SendDirection);

    QByteArray block = this->file->read(2000);
    this->transferred += block.size();

    if(block.size() == 0) //finished
    {
        emit finishedSending(this->id);
    }

    return block;
}

void TransferBox::cancelTransfer() {}
void TransferBox::pauseTransfer() {}
void TransferBox::resumeTransfer() {}



FileTransfer::FileTransfer(QWidget *parent)
        : QWidget(parent)
{
    this->sendingIdUpto = 0;
    this->lay = new QVBoxLayout(this);

    this->sendFileButton = new QPushButton(tr("Send a file"), this);
        QObject::connect(this->sendFileButton, SIGNAL(clicked()), this, SLOT(locateFile()));
        this->lay->addWidget(this->sendFileButton);


    this->setLayout(this->lay);
}

void FileTransfer::loopSend()
{
    QByteArray data;

    for(QMap<int, TransferBox*>::const_iterator i = sendingTransfers.constBegin(); i != sendingTransfers.end() ; ++i)
    {
        data = i.value()->send();
        if(!data.isEmpty())
        {
            QByteArray bytesToSend;
            QDataStream str(&bytesToSend , QIODevice::WriteOnly);
            str << (quint8) FileTransfer::FileDataFlag;
            str << (quint32) i.key();
            str << data;
            emit sendData(bytesToSend);
        }
    }
    QTimer::singleShot(0, this, SLOT(loopSend()));
    return;
}


void FileTransfer::locateFile()
{
    QString filepath = QFileDialog::getOpenFileName(this, tr("Locate the file you wish to send"));
    if(filepath.isNull() || filepath.isEmpty())
        return; //faggots wasting our god damn time

    QFileInfo zeFile(filepath);

    if(!zeFile.isFile())
    {
        QMessageBox::information(this, tr("Error!"), tr("%1 is not a file?").arg(filepath));
        return;
    }

    if(!zeFile.isReadable())
    {
        QMessageBox::information(this, tr("Error!"), tr("Unable to read file: %1").arg(filepath));
        return;
    }

    quint64 filesize = zeFile.size();
    if(filesize == 0)
    {
        QMessageBox::information(this, tr("Error!"), tr("Filesize is zero. Trying to read a device or something? %1").arg(filepath));
        return;
    }


    TransferBox *box = new TransferBox(++(this->sendingIdUpto), filepath, this);
        QObject::connect(box, SIGNAL(finishedSending(int)), this, SLOT(removeSending(int)));
        this->sendingTransfers.insert(this->sendingIdUpto, box);
        this->lay->addWidget(box);


     QByteArray data;
     QDataStream stream(&data, QIODevice::WriteOnly);

     stream << (quint8) FileTransfer::FileInitiateFlag;
     stream << (quint32) this->sendingIdUpto;
     stream << (quint64) filesize;
     stream << zeFile.fileName();

     emit sendData(data);
     QTimer::singleShot(0, this, SLOT(loopSend()));
}

void FileTransfer::removeReceiving(int id)
{
    this->receivingTransfers.value(id)->deleteLater();
    this->receivingTransfers.remove(id);
}


void FileTransfer::removeSending(int id)
{
    qDebug() << "Ok, removing: " << id;
    //this->sendingTransfers.value(id)->deleteLater();
    this->sendingTransfers.remove(id);
}

void FileTransfer::receiveData(QByteArray data)
{
    QDataStream stream(&data, QIODevice::ReadOnly);
    stream.setVersion(QDataStream::Qt_4_5);


    FileTransfer::TransferFlag transferType;

    //what's the proper syntax for this? A nicer way?
    quint8 tmpVar;
    stream >> tmpVar;
    transferType = (FileTransfer::TransferFlag) tmpVar;
    //?

    if(transferType == FileTransfer::FileInitiateFlag)
    {
        quint32 fileId;
        quint64 fileSize;
        QString fileName;
        stream >> fileId;
        stream >> fileSize;
        stream >> fileName;

        //TODO: improve fileName shit drastically to make it safe
        //something like jesus.jpg to jesus_1.jpg would be ideal
        while(QFile::exists(fileName))
        {
            fileName = "x" + fileName;
        }
qDebug() << "file init recieved. id: " << fileId << " fileName: " << fileName << " filesize: " << fileSize;

        TransferBox *box = new TransferBox(fileId, fileName, fileSize, this);
            QObject::connect(box, SIGNAL(finishedSending(int)), this, SLOT(removeSending(int)));
            this->receivingTransfers.insert(fileId, box);
            this->lay->addWidget(box);


    }
    else if(transferType == FileTransfer::FileFinishFlag)
    {
        quint32 fileId;
        stream >> fileId;
        qDebug() << "File Id: " << fileId << " is done";
    }
    else if(transferType == FileTransfer::FileDataFlag)
    {
        quint32 fileId;
        stream >> fileId;

        QByteArray data;
        stream >> data;
        Q_ASSERT(this->receivingTransfers.contains(fileId));

        this->receivingTransfers[fileId]->receive(data);

    }
    else
    {
        qFatal("Hmm. Unsupport FileTransfer type.");

    }
}

